import React from "react";

import {
  ApolloClient,
  InMemoryCache,
  createHttpLink,
  ApolloProvider,
  useQuery,
  useLazyQuery,
  useMutation,
  useApolloClient,
} from "@apollo/client";
import { setContext } from "@apollo/client/link/context";

// 禁用 cache
const defaultOptions = {
  watchQuery: {
    fetchPolicy: "no-cache",
    errorPolicy: "ignore",
  },
  query: {
    fetchPolicy: "no-cache",
    errorPolicy: "all",
  },
};

const httpLink = createHttpLink({
  uri: process.env.REACT_APP_BASE_URL,
});

const authLink = setContext((_, options) => {
  // 有 token 才设置
  const token = null;
  if (token) {
    // options.headers.authorization = token;
    options = {
      ...options,
      headers: {
        ...options.headers,
        authorization: token,
      },
    };
  }

  return options;
});

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
  defaultOptions,
});

/**
 * 获取包装 gql 后的接口名 *
 * @param {Object} gql 包装后的 gql
 * @return {String} 接口名
 */
const getGqlName = (gql) => {
  try {
    const { selectionSet } = gql.definitions[0];
    return selectionSet.selections[0].name.value;
  } catch (e) {
    console.error(e);
  }
};

/**
 * 给 header 增加 targetmethod 字段，供后端做接口白名单用，
 *
 * @param {String} gql gql对象
 * @param {Object} 额外配置项
 * @return {Object} 给 header 增加 targetmethod 字段的配置项
 */
const setTargetMethod = (gql, option) => {
  let result = {};
  if (option) {
    if (!(option instanceof Object)) {
      throw new Error("second param must be object");
    } else {
      result = option;
    }
  }

  return {
    ...result,
    context: {
      ...result.context,
      headers: {
        ...result.headers,
        targetmethod: getGqlName(gql), // 所有接口需要使用该字段用
      },
    },
  };
};

/**
 * 自定义 query hook
 *
 * @param {String} gql gql语句
 * @param {Object} option useQuery 配置项
 * @return {function} 返回 useQuery 函数结果
 */
export const useApolloQuery = (gql, option) => {
  return useQuery(gql, setTargetMethod(gql, option));
};

/**
 * 自定义 client query hook
 *
 * @param {String} gql gql语句
 * @param {Object} option useQuery 配置项
 * @return {function} 返回 useQuery 函数结果
 */
export const useApolloClientQuery = () => {
  const client = useApolloClient();

  return (options) => {
    const { query } = options; // 获取 gql
    return client.query({
      ...setTargetMethod(query, options),
    });
  };
};

/**
 * 自定义 lazyQuery hook
 *
 * @param {String} gql gql语句
 * @param {Object} option useQuery 配置项
 * @return {function} 返回 useLazyQuery 函数结果
 */
export const useApolloLazyQuery = (gql, option) => {
  return useLazyQuery(gql, setTargetMethod(gql, option));
};

/**
 * 自定义 mutation hook
 *
 * @param {String} gql gql语句
 * @param {Object} option useMutation 配置项
 * @return {function} 返回 useMutation 函数结果
 */
export const useApolloMutation = (gql, option) => {
  return useMutation(gql, setTargetMethod(gql, option));
};

// react 使用
export default function MyApolloClient({ children }) {
  return <ApolloProvider client={client}>{children}</ApolloProvider>;
}
